#include "branch_contour.h"
#include <cstdlib>


#define PASSESLIMIT 5

Position ContourExtractorBranch::findFirstPoint(Grey_Image *rotuled){

    //raster para achar primeiro pixel, identico ao tradicional
    Position point;
    point.y=-1;
    point.x=-1;
    for(unsigned int i = 0; i < rotuled->getHeight(); i++)
        for(unsigned int j = 0; j < rotuled->getWidth() ; j++)
            if(rotuled->getPixel(j,i) > 2){
                point.x = j-1;
                point.y = i;
                lastlabel=rotuled->getPixel(j,i);
                return point;
            }
    return point;
}

Position ContourExtractorBranch::findNextPoint(Grey_Image *rotuled, bool crossingout){
    int r;  //iterator
    int d_cp;    // direction: current to previous pixel
    int de, di;
    Position Pe, Pi,Pn,candidate;
    Pn=current;
    d_cp = invertChainCode(d_pc);
    bool searchcondition;

    int lblsoma=2;

    for(r = 0; r < 7; r++){
        de = (d_cp + r)%8;
        di = (d_cp + r + 1)%8;
        Pe = current+neighbours[de];
        Pi = current+neighbours[di];
        if(Pi.isInside(-1,-1,width,height) && Pe.isInside(-1,-1,width,height)){

            //evita retornar no cruzamento
            if(crossingout){
                if(rotuled->getPixel(Pi) == 4)
                    searchcondition=false;
                else
                    searchcondition=true;
            }
            else
                searchcondition=true;

            if((rotuled->getPixel(Pi) != 0) && (rotuled->getPixel(Pe) == 0) && searchcondition ){
                lastanteriorlabel=lastlabel;
                lastlabel=currlabel;
                currlabel=rotuled->getPixel(Pi);
                currobjpix=Pi;

                //probe

                if((unsigned)lastlabel!=(unsigned)rotuled->getPixel(Pi)&&rotuled->getPixel(Pi)>5){
                    for(int j=0;j<8;j++)
                        if(rotuled->getPixel(current+neighbours[j])==4){
                            hidecrossing=true;
                            break;
                        }
                }

                //cout << "lastlabel: " << lastlabel << "\n";
                //cout << "currentlabel: " << currlabel << "\n";

                if(lastlabel!=currlabel){//crepe
                    if(lastlabel!=lblsoma && currlabel == lblsoma){
                        cout << "oops! Different labels, but next one is a soma pixel\n";

                        //getchar();
                        }
                    else if(lastlabel == lblsoma && currlabel != lblsoma){
                        cout << "oops! Different labels, but last one was a soma pixel\n";
                        cout << "currlabel: " << currlabel << endl;
                        cout << "lastlabel: " << lastlabel << endl;
                        cout << "lastanteriorlabel: " << lastanteriorlabel << endl;
                        cout << "Pe: " << Pe.x << "," << Pe.y << endl;
                        cout << "dontrepeat " << dontrepeat.x << "," << dontrepeat.y << endl;

                        //getchar();
                        }
                    else if(currlabel == 3 || lastlabel ==3){  //coisa omitida atançãop
                        cout << "oops! Different labels! It seems a crossing. Checking it";
                        cout << "\nNo, it is just a bifurcation! Contour outwards!\n";

                    }
                    else if(currlabel == 4){
                        cout << "\nYes, it is a crossing!\n";
                        cout << "\nCertainly there is a pointer nearby\n";
                        crossing=true;;
                        Pn=next;
                        d_cn=d_pc;
                    }
                 }

                if(!crossing)
                    d_cn = de;
                Pn=Pe;

                //condições para parar no primeiro pixel(adicionei uma excessão devido a um caso especial)
                if(lastlabel!=lblsoma){
                    if(currlabel==lblsoma){
                        if(lastanteriorlabel==lblsoma)
                            continue;
                        else break;
                    }
                    else{
                        if(Pn!=dontrepeat)
                            break;
                        else continue;
                    }
                }
                else{
                    if(currlabel==lblsoma||Pn==dontrepeat)
                        continue;
                    else
                        break;

                }

            }
        }
        else{
            if(Pi.isInside(-1,-1,width,height)){
                if(rotuled->getPixel(Pi) != 0){
                    d_cn = de;
                    Pn=Pe;
                }
            }
        }
    }
    return Pn;
}


Contour* ContourExtractorBranch::getContour(Grey_Image *rotuled,PositionMap *pointer){
    Contour *contour;
    height=rotuled->getHeight();
    width=rotuled->getWidth();
    neighbours = createNeighbours();
    contour = contourFollower(rotuled,pointer);
    contour->setClosed(true);
    contour->setLabel(1);
    contour->setOrigin(1);
    contour->setPixelSize(rotuled->getPixelSize());
    contour->setPixelLenght(contour->points.size());
    if(neighbours!=NULL)
        delete [] neighbours;
    return contour;
}

Contour* ContourExtractorBranch::contourFollower(Grey_Image *rotuled,PositionMap *pointer){
    Contour *contour = new Contour(rotuled);
    passcounter = new Grey_Image(rotuled->getHeight(),rotuled->getWidth(),65500);

    int passes=0;

    first = findFirstPoint(rotuled);


    if(first.isPositive()){
        contour->addPoint(first);
        passcounter->setPixel(first,passcounter->getPixel(first)+1);
        current = first;

        for(int i = 4; i < 8; i++){
            if((first+neighbours[i]).isInside(-1,-1,width,height) && (first+neighbours[(i+1)%8]).isInside(-1,-1,width,height))
                if(((rotuled->getPixel(first+neighbours[i])) == 0) && ((rotuled->getPixel(first+neighbours[(i+1)%8])) != 0)){
                        next=first+neighbours[i];
                        currobjpix=first+neighbours[(i+1)%8];
                        d_cn = i;
                        break;
                }
        }

        crossing=false;
        hidecrossing=false;
        currlabel=rotuled->getPixel(currobjpix);


        while(first!=next){
            crossing=false;
            hidecrossing=false;
            dontrepeat=current;
            current=next;
            lastobjpix=currobjpix;
            contour->addPoint(next);
            passcounter->setPixel(next,passcounter->getPixel(next)+1);
            if(passcounter->getPixel(next)>=5)
                passes++;
            if(passes>PASSESLIMIT){
                contour->img->drawCircle(next.x,next.y,7);
                return contour;
            }
            d_pc = d_cn;
            next = findNextPoint(rotuled,false);
            cout << "continuando: " << next.x << "-" << next.y <<"\n";
            //getchar();

            if(hidecrossing){
                next=contour->getLastPoint();
                contour->removelastPoint();
                current=contour->getLastPoint();
                for(unsigned int k=0;k<8;k++){
                    if(current+neighbours[k]==next){
                        d_cn=k;
                    }
                }
                crossing=true;
            }


            //findcrossing
            if(crossing&&first!=next){
                //cout << "cruzamento\n";

                crossing=false;
                bool foundpointer=false;

                //procura ponteiros perto
                for(int i=0;i<7;i++){
                    if(pointer->getPosition(current+neighbours[i])!=Position(-1,-1)){
                        continuepoint=pointer->getPosition(current+neighbours[i]);
                        foundpointer=true;
                        break;
                    }
                }
                if(foundpointer==false) {
                    cout << "Nearby pointer not found";
                    continue;
                }

                unsigned int direction;
                Position lastcontourpixel=contour->points.back();


                //recording last direction
                for(unsigned int i=0;i<7;i++){
                    if(current==(lastobjpix+neighbours[i])){
                         direction=(i+4)%8;
                        break;
                    }
                }


                //começa procurando perto da direção antiga

                for(unsigned int i=direction;i<direction+7;i++){
                    if( ( rotuled->getPixel(continuepoint+neighbours[((i+1)%8)])==(unsigned)lastlabel) && (rotuled->getPixel(continuepoint+neighbours[((i)%8)])==0) ){
                        continuepoint=continuepoint+neighbours[((i)%8)];
                        break;
                    }

                }

                cout << "corrente: " << current.x << "," << current.y << "conexão: " << continuepoint.x << "," << continuepoint.y << endl;

                bresenham(contour,current,continuepoint);

                //acerta a direcao para o proximo ponto
                current=continuepoint;
                currlabel=lastlabel;
                d_pc=d_cn;


                next = findNextPoint(rotuled,true);
                cout << "continuando depois da ponte: " << next.x << "-" << next.y <<"\n";
                //getchar();


            }

        }
        return contour;
    }
    else{
        delete contour;
        return NULL;
    }
}

void swap(int* one,int* two){
    int temp=*one;
    *one=*two;
    *two=temp;
}

void ContourExtractorBranch::bresenham(Contour* contour,Position first,Position last){

   //from wikipedia
   int x0,x1,y0,y1;
   x0=first.x;
   y0=first.y;
   x1=last.x;
   y1=last.y;


   int Dx = x1 - x0;
   int Dy = y1 - y0;
   int steep = (abs(Dy) >= abs(Dx));
   if (steep) {
       swap(&x0, &y0);
       swap(&x1, &y1);
       // recompute Dx, Dy after swap
       Dx = x1 - x0;
       Dy = y1 - y0;
   }
   int xstep = 1;
   if (Dx < 0) {
       xstep = -1;
       Dx = -Dx;
   }
   int ystep = 1;
   if (Dy < 0) {
       ystep = -1;
       Dy = -Dy;
   }
   int TwoDy = 2*Dy;
   int TwoDyTwoDx = TwoDy - 2*Dx; // 2*Dy - 2*Dx
   int E = TwoDy - Dx; //2*Dy - Dx
   int y = y0;
   int xDraw, yDraw;
   for (int x = x0; x != x1; x += xstep) {
       if (steep) {
           xDraw = y;
           yDraw = x;
       } else {
           xDraw = x;
           yDraw = y;
       }
       // plot
       contour->addPoint(Position(xDraw,yDraw));
       passcounter->setPixel(Position(xDraw,yDraw),passcounter->getPixel(Position(xDraw,yDraw))+1);
       // next
       if (E > 0) {
           E += TwoDyTwoDx; //E += 2*Dy - 2*Dx;
           y = y + ystep;
       } else {
           E += TwoDy; //E += 2*Dy;
       }
   }

}

int ContourExtractorBranch::invertChainCode(int code){
    if(code > 3)
        return code - 4;
    else return code + 4;
}
